using System;
using System.Collections.Generic;
using Oracle.ManagedDataAccess.Client;
using OracleInternal.Common;
using OracleInternal.ServiceObjects;

namespace OracleInternal.TTC.Accessors
{
	internal abstract class Accessor
	{
		protected bool m_bForBind;

		internal bool m_bForReturningParameter;

		internal SqlStatementType m_statementType;

		internal OraType m_internalType;

		protected int m_internalTypeMaxLength;

		internal OraType m_definedColumnType;

		internal ColumnDescribeInfo m_colMetaData;

		internal MarshallingEngine m_marshallingEngine;

		protected int m_byteLength;

		internal int m_lastRowProcessed;

		internal List<ArraySegment<byte>> m_colDataSegments;

		internal List<List<ArraySegment<byte>>> m_RowDataSegments;

		internal List<int> m_totalLengthOfData;

		internal List<ArraySegment<byte>> m_LastRowDataSegments;

		internal int m_LastRowtotalLengthOfData;

		internal bool m_bNullByDescribe;

		internal bool m_bReceivedOutValueFromServer;

		internal bool NoRowsAffected => m_lastRowProcessed <= 0;

		internal Accessor(ColumnDescribeInfo colMetaData, MarshallingEngine marshallingEngine, bool bForBind)
		{
			m_marshallingEngine = marshallingEngine;
			m_bForBind = bForBind;
			m_colMetaData = colMetaData;
			Initialize();
		}

		internal static Accessor CreateAccessorForDefine(MarshallingEngine mEngine, ColumnDescribeInfo colMetaData, int initialLongFetchSize, long initialLOBFetchSize, long initialJSONFetchSize, bool bDefineDone, bool bLOBArrayFetchRequired, int numRowsRequested)
		{
			return CreateAccessor(mEngine, colMetaData, bForBind: false, initialLongFetchSize, initialLOBFetchSize, initialJSONFetchSize, bDefineDone, bLOBArrayFetchRequired, numRowsRequested);
		}

		internal static Accessor CreateAccessorForBind(MarshallingEngine mEngine, ColumnDescribeInfo colMetaData, SqlStatementType stmtType, int initialLongFetchSize, long initialJSONFetchSize, int numRowsRequested)
		{
			Accessor accessor = CreateAccessor(mEngine, colMetaData, bForBind: true, initialLongFetchSize, 0L, initialJSONFetchSize, bDefineDone: false, bLOBArrayFetchRequired: false, numRowsRequested);
			accessor.m_statementType = stmtType;
			return accessor;
		}

		private static Accessor CreateAccessor(MarshallingEngine mEngine, ColumnDescribeInfo colMetaData, bool bForBind, int initialLongFetchSize, long initialLOBFetchSize, long initialJSONFetchSize, bool bDefineDone, bool bLOBArrayFetchRequired = false, int numRowsRequested = 0)
		{
			switch (colMetaData.m_dataType)
			{
			case 1:
			case 9:
			case 96:
				return new TTCVarcharAccessor(colMetaData, mEngine, bForBind);
			case 2:
			case 4:
			case 6:
				return new TTCNumberAccessor(colMetaData, mEngine, bForBind);
			case 12:
			case 180:
			case 181:
			case 187:
			case 188:
			case 231:
			case 232:
				return new TTCDateTimeAccessor(colMetaData, mEngine, bForBind);
			case 102:
			case 116:
				return new TTCRefCursorAccessor(colMetaData, mEngine);
			case 112:
			case 113:
			case 114:
				return new TTCLobAccessor(colMetaData, mEngine, bForBind, initialLOBFetchSize, bDefineDone, bLOBArrayFetchRequired, numRowsRequested);
			case 119:
				return new TTCJsonAccessor(colMetaData, mEngine, bForBind, initialJSONFetchSize, bDefineDone, bLOBArrayFetchRequired, numRowsRequested);
			case 8:
			case 24:
				return new TTCLongAccessor(colMetaData, mEngine, bForBind, initialLongFetchSize);
			case 182:
			case 183:
			case 189:
			case 190:
				return new TTCIntervalTypeAccessor(colMetaData, mEngine, bForBind);
			case 23:
				return new TTCRawAccessor(colMetaData, mEngine, bForBind);
			case 101:
				return new TTCBinaryDoubleAccessor(colMetaData, mEngine, bForBind);
			case 100:
				return new TTCBinaryFloatAccessor(colMetaData, mEngine, bForBind);
			case 11:
			case 208:
				return new TTCRowIdAccessor(colMetaData, mEngine, bForBind);
			case 252:
				return new TTCPLSQLBooleanAccessor(colMetaData, mEngine, bForBind);
			case 109:
				if (colMetaData.bIsXmlType)
				{
					return new TTCXmlTypeAccessor(colMetaData, mEngine, bForBind);
				}
				throw new OracleException(ResourceStringConstants.CMD_TYPE_NOT_SUPPORTED, string.Empty, string.Empty, OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.CMD_TYPE_NOT_SUPPORTED));
			default:
				throw new OracleException(ResourceStringConstants.CMD_TYPE_NOT_SUPPORTED, string.Empty, string.Empty, OracleStringResourceManager.GetErrorMesg(ResourceStringConstants.CMD_TYPE_NOT_SUPPORTED));
			}
		}

		internal virtual void InitForDataAccess(int max_len)
		{
			if (max_len > 0 && max_len < m_internalTypeMaxLength)
			{
				m_internalTypeMaxLength = max_len;
			}
			m_byteLength = m_internalTypeMaxLength;
		}

		internal virtual void Unimplemented(string methodName)
		{
			throw new Exception(methodName + " not implemented for " + GetType());
		}

		internal virtual string GetString(int currentRow, char[] charArrayFromPooler)
		{
			Unimplemented("GetString");
			return null;
		}

		internal int GetCharsFromBuffer(byte[] rawData, int rawDataLen, long fieldOffset, char[] buffer, int bufferOffset, int noOfCharsReqd, byte charSetForm)
		{
			int charCount = 0;
			if (rawData.Length != 0)
			{
				int byteOffset = (int)fieldOffset;
				charCount = noOfCharsReqd;
				if (charSetForm != 2)
				{
					if (m_marshallingEngine.m_dbCharSetConv.MaxBytesPerChar > 1 && fieldOffset > 0)
					{
						byteOffset = m_marshallingEngine.m_dbCharSetConv.GetBytesOffset(rawData, (int)fieldOffset, rawDataLen, noOfCharsReqd);
					}
					m_marshallingEngine.m_dbCharSetConv.ConvertBytesToChars(rawData, byteOffset, rawDataLen, buffer, bufferOffset, ref charCount);
				}
				else
				{
					if (m_marshallingEngine.m_nCharSetConv.MaxBytesPerChar > 1 && fieldOffset > 0)
					{
						byteOffset = m_marshallingEngine.m_dbCharSetConv.GetBytesOffset(rawData, (int)fieldOffset, rawDataLen, noOfCharsReqd);
					}
					m_marshallingEngine.m_nCharSetConv.ConvertBytesToChars(rawData, byteOffset, rawDataLen, buffer, bufferOffset, ref charCount);
				}
			}
			return charCount;
		}

		internal string GetString(byte[] rawBytesToConvert, int byteOffset, int length, byte charSetForm, char[] charArrayForConversion)
		{
			if (charSetForm != 2)
			{
				return m_marshallingEngine.m_dbCharSetConv.ConvertBytesToString(rawBytesToConvert, byteOffset, length, charArrayForConversion);
			}
			return m_marshallingEngine.m_nCharSetConv.ConvertBytesToString(rawBytesToConvert, byteOffset, length, charArrayForConversion);
		}

		internal virtual string GetString(int currentRow, byte charSetForm, char[] charArrayFromPooler)
		{
			Unimplemented("GetString");
			return null;
		}

		internal virtual string GetString(DataUnmarshaller dataUnmarshaller, int currentRow, int columnIndex)
		{
			Unimplemented("GetString");
			return null;
		}

		internal virtual string GetString(DataUnmarshaller dataUnmarshaller, int currentRow, int columnIndex, byte charSetForm)
		{
			Unimplemented("GetString");
			return null;
		}

		internal virtual int GetInt(DataUnmarshaller dataUnmarshaller, int currentRow, int columnIndex)
		{
			Unimplemented("GetInt");
			return -1;
		}

		internal virtual int GetInt(int currentRow)
		{
			Unimplemented("GetInt");
			return -1;
		}

		internal virtual double GetDouble(int currentRow)
		{
			Unimplemented("GetDouble");
			return -1.0;
		}

		internal virtual double GetDouble(DataUnmarshaller dataUnmarshaller, int currentRow, int columnIndex, out byte[] byteRep)
		{
			byteRep = null;
			Unimplemented("GetDouble");
			return -1.0;
		}

		internal virtual double GetDouble(DataUnmarshaller dataUnmarshaller, int currentRow, int columnIndex)
		{
			Unimplemented("GetDouble");
			return -1.0;
		}

		internal virtual float GetFloat(int currentRow)
		{
			Unimplemented("GetFloat");
			return -1f;
		}

		internal virtual float GetFloat(DataUnmarshaller dataUnmarshaller, int currentRow, int columnIndex)
		{
			Unimplemented("GetDouble");
			return -1f;
		}

		internal virtual float GetFloat(DataUnmarshaller dataUnmarshaller, int currentRow, int columnIndex, out byte[] byteRep)
		{
			byteRep = null;
			Unimplemented("GetDouble");
			return -1f;
		}

		internal virtual decimal GetDecimal(int currentRow)
		{
			Unimplemented("GetDecimal");
			return -1m;
		}

		internal virtual DateTime GetDateTime(int currentRow)
		{
			Unimplemented("GetDate");
			return default(DateTime);
		}

		internal virtual DateTime GetDateTime(DataUnmarshaller dataUnmarshaller, int currentRow, int columnIndex)
		{
			Unimplemented("GetDate");
			return default(DateTime);
		}

		internal virtual DateTime GetDateTime(DataUnmarshaller dataUnmarshaller, int currentRow, int columnIndex, out byte[] byteRep)
		{
			byteRep = null;
			Unimplemented("GetDate");
			return default(DateTime);
		}

		internal virtual void GetInternalDataRef(DataUnmarshaller dataUnmarshaller, int currentRow, int columnIndex, ref byte[] dataByteRep, ref int dataOffset, ref int dataLength)
		{
			try
			{
				dataUnmarshaller.StartAccumulatingColumnData(currentRow, columnIndex, m_colDataSegments);
				dataUnmarshaller.UnmarshalCLR_ScanOnly(m_colMetaData.m_maxLength, ref dataLength);
				if (dataLength > 0)
				{
					if (m_colDataSegments.Count > 1)
					{
						dataByteRep = new byte[dataLength];
						CopyDataToUserBuffer(m_colDataSegments, 0, dataByteRep, 0, dataLength);
					}
					else
					{
						dataByteRep = m_colDataSegments[0].Array;
						dataOffset = m_colDataSegments[0].Offset;
					}
				}
			}
			finally
			{
				m_colDataSegments.Clear();
				dataUnmarshaller.m_bAccumulateByteSegments = false;
				dataUnmarshaller.m_dataSegments = null;
			}
		}

		internal virtual byte[] GetByteRepresentation(DataUnmarshaller dataUnmarshaller, int currentRow, int columnIndex)
		{
			byte[] result = null;
			int length = 0;
			try
			{
				dataUnmarshaller.StartAccumulatingColumnData(currentRow, columnIndex, m_colDataSegments);
				dataUnmarshaller.UnmarshalCLR_ScanOnly(m_colMetaData.m_maxLength, ref length);
				if (length > 0)
				{
					result = new byte[length];
					CopyDataToUserBuffer(m_colDataSegments, 0, result, 0, length);
					return result;
				}
				return result;
			}
			finally
			{
				m_colDataSegments.Clear();
				dataUnmarshaller.m_bAccumulateByteSegments = false;
				dataUnmarshaller.m_dataSegments = null;
			}
		}

		internal virtual byte[] GetByteRepresentation(int currentRow)
		{
			byte[] array = null;
			int num = m_totalLengthOfData[currentRow];
			if (num > 0)
			{
				List<ArraySegment<byte>> list = m_RowDataSegments[currentRow];
				if (list != null)
				{
					array = new byte[num];
					CopyDataToUserBuffer(list, 0, array, 0, num);
				}
			}
			return array;
		}

		internal virtual bool IsNullIndicatorSet(int currentRow)
		{
			bool result = m_bNullByDescribe;
			if (m_totalLengthOfData.Count > currentRow)
			{
				result = m_totalLengthOfData[currentRow] == 0;
			}
			return result;
		}

		internal virtual bool IsNullIndicatorSet(DataUnmarshaller dataUnmarshaller, int columnCount, int currentRow, int columnIndex)
		{
			bool result = m_bNullByDescribe;
			if (!m_bNullByDescribe)
			{
				result = -1 == dataUnmarshaller.m_colDataStartOffset[currentRow * columnCount + columnIndex];
			}
			return result;
		}

		internal virtual void UnmarshalColumnData()
		{
			if (!m_bNullByDescribe)
			{
				try
				{
					m_marshallingEngine.m_oraBufRdr.m_bParsingColumnData = true;
					m_marshallingEngine.m_oraBufRdr.m_bMarkStartOffsetForColData = true;
					switch (m_definedColumnType)
					{
					case OraType.ORA_CHARN:
					case OraType.ORA_VARCHAR:
					case OraType.ORA_CHAR:
						m_marshallingEngine.UnmarshalCLR_ColData(int.MaxValue);
						break;
					case OraType.ORA_RAW:
					case OraType.ORA_IBFLOAT:
					case OraType.ORA_IBDOUBLE:
					case OraType.ORA_INTERVAL_YM_DTY:
					case OraType.ORA_INTERVAL_DS_DTY:
					case OraType.ORA_INTERVAL_YM:
					case OraType.ORA_INTERVAL_DS:
						m_marshallingEngine.UnmarshalCLR_ColData(m_colMetaData.m_maxLength);
						break;
					case OraType.ORA_NUMBER:
					case OraType.ORA_FLOAT:
					case OraType.ORA_VARNUM:
						m_marshallingEngine.UnmarshalCLR_ColData(21);
						break;
					case OraType.ORA_DATE:
						m_marshallingEngine.UnmarshalCLR_ColData(7);
						break;
					case OraType.ORA_TIMESTAMP_DTY:
					case OraType.ORA_TIMESTAMP:
					case OraType.ORA_TIMESTAMP_LTZ_DTY:
					case OraType.ORA_TIMESTAMP_LTZ:
						m_marshallingEngine.UnmarshalCLR_ColData(11);
						break;
					case OraType.ORA_TIMESTAMP_TZ_DTY:
					case OraType.ORA_TIMESTAMP_TZ:
						m_marshallingEngine.UnmarshalCLR_ColData(13);
						break;
					case OraType.ORA_RESULTSET:
						throw new InvalidOperationException();
					default:
						throw new Exception("UnmarshalColumnData: Unimplemented type");
					}
				}
				finally
				{
					m_marshallingEngine.m_oraBufRdr.m_bMarkStartOffsetForColData = false;
					m_marshallingEngine.m_oraBufRdr.m_bParsingColumnData = false;
				}
			}
			else
			{
				m_marshallingEngine.m_oraBufRdr.m_colDataStartOffset[m_marshallingEngine.m_oraBufRdr.m_colDataStartOffsetIndexToUpdate] = -1;
			}
			m_lastRowProcessed++;
		}

		internal void AddNullForData()
		{
			if (m_RowDataSegments != null && m_RowDataSegments.Count > m_lastRowProcessed)
			{
				m_RowDataSegments[m_lastRowProcessed] = null;
				m_totalLengthOfData[m_lastRowProcessed] = 0;
			}
			else
			{
				m_RowDataSegments.Add(null);
				m_totalLengthOfData.Add(0);
			}
			m_lastRowProcessed++;
		}

		internal virtual bool UnmarshalOneRow()
		{
			List<ArraySegment<byte>> dataSegments = null;
			int length = 0;
			bool flag = true;
			if (!m_bNullByDescribe)
			{
				try
				{
					flag = m_marshallingEngine.m_oraBufRdr.StartAccumulatingColumnData(m_RowDataSegments, m_lastRowProcessed);
					switch (m_definedColumnType)
					{
					case OraType.ORA_CHARN:
					case OraType.ORA_VARCHAR:
					case OraType.ORA_RAW:
					case OraType.ORA_CHAR:
					case OraType.ORA_IBFLOAT:
					case OraType.ORA_IBDOUBLE:
					case OraType.ORA_INTERVAL_YM_DTY:
					case OraType.ORA_INTERVAL_DS_DTY:
					case OraType.ORA_INTERVAL_YM:
					case OraType.ORA_INTERVAL_DS:
					case OraType.ORA_BOOLEAN:
						m_marshallingEngine.UnmarshalCLR_ScanOnly(m_colMetaData.m_maxLength, out dataSegments, ref length);
						break;
					case OraType.ORA_NUMBER:
					case OraType.ORA_FLOAT:
					case OraType.ORA_VARNUM:
						m_marshallingEngine.UnmarshalCLR_ScanOnly(21, out dataSegments, ref length);
						break;
					case OraType.ORA_DATE:
						m_marshallingEngine.UnmarshalCLR_ScanOnly(7, out dataSegments, ref length);
						break;
					case OraType.ORA_TIMESTAMP_DTY:
					case OraType.ORA_TIMESTAMP:
					case OraType.ORA_TIMESTAMP_LTZ_DTY:
					case OraType.ORA_TIMESTAMP_LTZ:
						m_marshallingEngine.UnmarshalCLR_ScanOnly(11, out dataSegments, ref length);
						break;
					case OraType.ORA_TIMESTAMP_TZ_DTY:
					case OraType.ORA_TIMESTAMP_TZ:
						m_marshallingEngine.UnmarshalCLR_ScanOnly(13, out dataSegments, ref length);
						break;
					case OraType.ORA_OCICLobLocator:
					case OraType.ORA_OCIBLobLocator:
					case OraType.ORA_OCIBFileLocator:
					case OraType.ORA_OCIJSON:
					{
						int num = (int)m_marshallingEngine.UnmarshalUB4();
						if (num > 0)
						{
							m_marshallingEngine.UnmarshalCLR_ScanOnly(num, out dataSegments, ref length);
						}
						break;
					}
					case OraType.ORA_RESULTSET:
						throw new InvalidOperationException();
					default:
						throw new Exception("UnmarshalOneRow: Unimplemented type");
					}
				}
				finally
				{
					m_marshallingEngine.m_oraBufRdr.StopAccumulatingColumnData();
					if (m_bForBind && -1 == m_marshallingEngine.ProcessIndicator(length <= 0, length))
					{
						length = 0;
					}
					if (flag)
					{
						m_RowDataSegments.Add(dataSegments);
						m_totalLengthOfData.Add(length);
					}
					else
					{
						m_RowDataSegments[m_lastRowProcessed] = dataSegments;
						m_totalLengthOfData[m_lastRowProcessed] = length;
					}
				}
			}
			else
			{
				m_RowDataSegments.Add(dataSegments);
				m_totalLengthOfData.Add(length);
			}
			m_lastRowProcessed++;
			return false;
		}

		internal void CopyRow_ScanOnly()
		{
			if (m_bNullByDescribe)
			{
				return;
			}
			int num = 0;
			List<ArraySegment<byte>> list;
			if (m_lastRowProcessed == 0)
			{
				list = m_LastRowDataSegments;
				num = m_LastRowtotalLengthOfData;
			}
			else
			{
				list = m_RowDataSegments[m_lastRowProcessed - 1];
				num = m_totalLengthOfData[m_lastRowProcessed - 1];
			}
			List<ArraySegment<byte>> list2 = new List<ArraySegment<byte>>();
			if (list != null && list.Count > 0)
			{
				for (int i = 0; i < list.Count; i++)
				{
					list2.Add(list[i]);
				}
			}
			if (m_RowDataSegments.Count > m_lastRowProcessed)
			{
				m_RowDataSegments[m_lastRowProcessed] = list2;
				m_totalLengthOfData[m_lastRowProcessed] = num;
			}
			else
			{
				m_RowDataSegments.Add(list2);
				m_totalLengthOfData.Add(num);
			}
			m_lastRowProcessed++;
		}

		internal virtual object GetValue()
		{
			return null;
		}

		internal static int CopyDataToUserBuffer(List<ArraySegment<byte>> dataSegments, int dataOffset, byte[] userBuffer, int userBuffOffset, int dataLength)
		{
			if (dataSegments == null || dataSegments.Count <= 0)
			{
				return 0;
			}
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			bool flag = dataOffset > 0;
			int num5 = ((dataLength < userBuffer.Length - userBuffOffset) ? dataLength : (userBuffer.Length - userBuffOffset));
			int num6 = userBuffOffset;
			for (int i = 0; i < dataSegments.Count; i++)
			{
				ArraySegment<byte> arraySegment = dataSegments[i];
				num2 = arraySegment.Count;
				if (flag)
				{
					if (num2 <= dataOffset)
					{
						dataOffset -= num2;
						continue;
					}
					num4 = dataOffset;
					dataOffset = 0;
					flag = false;
				}
				num3 = num2 - num4;
				if (num3 >= num5)
				{
					Buffer.BlockCopy(arraySegment.Array, arraySegment.Offset + num4, userBuffer, num6, num5);
					num += num5;
					break;
				}
				Buffer.BlockCopy(arraySegment.Array, arraySegment.Offset + num4, userBuffer, num6, num3);
				num6 += num3;
				num5 -= num3;
				num += num3;
				num4 = 0;
			}
			return num;
		}

		internal static byte GetValueAt(List<ArraySegment<byte>> dataSegments, int dataOffset)
		{
			int num = 0;
			byte result = 0;
			int num2 = 0;
			bool flag = dataOffset > 0;
			int num3 = 0;
			while (num3 < dataSegments.Count)
			{
				ArraySegment<byte> arraySegment = dataSegments[num3];
				num = arraySegment.Count;
				if (flag)
				{
					if (num <= dataOffset)
					{
						dataOffset -= num;
						num3++;
						continue;
					}
					num2 = dataOffset;
					dataOffset = 0;
					flag = false;
				}
				result = arraySegment.Array[arraySegment.Offset + num2];
				break;
			}
			return result;
		}

		internal virtual void Initialize(ColumnDescribeInfo colMetaData, MarshallingEngine marshallingEngine, bool bForBind)
		{
			m_marshallingEngine = marshallingEngine;
			m_bForBind = bForBind;
			m_colMetaData = colMetaData;
			Initialize();
		}

		internal void Initialize()
		{
			if (m_bForBind)
			{
				if (m_totalLengthOfData != null)
				{
					m_totalLengthOfData.Clear();
				}
				else
				{
					m_totalLengthOfData = new List<int>();
				}
				if (m_RowDataSegments != null)
				{
					m_RowDataSegments.Clear();
				}
				else
				{
					m_RowDataSegments = new List<List<ArraySegment<byte>>>();
				}
			}
			else if (m_colDataSegments != null)
			{
				m_colDataSegments.Clear();
			}
			else
			{
				m_colDataSegments = new List<ArraySegment<byte>>();
			}
			m_lastRowProcessed = 0;
			m_LastRowDataSegments = null;
			m_LastRowtotalLengthOfData = 0;
			m_bReceivedOutValueFromServer = false;
			if (m_colMetaData != null)
			{
				m_internalType = (m_definedColumnType = (OraType)m_colMetaData.m_dataType);
				m_internalTypeMaxLength = m_colMetaData.m_maxLength;
				m_bNullByDescribe = m_colMetaData.m_maxLength <= 0;
			}
		}
	}
}
